昨天寫到「同一個錯誤犯第二次就不是意外,是習慣缺口」——
指的是設計文件與實作分岔,兩天撞到兩次。今天把那個缺口補掉。
打開 docs/superpowers/specs/2026-09-08-multiagent-tutor-design.md,
那是三期開工前的設計。從第 0 步之後就沒再動過。
一行一行比對現況,八處對不上。
直覺的做法是把錯的段落改成對的,文件就乾淨了。
我沒有這樣做。理由是:
這份文件記錄的不是「系統長什麼樣」,而是「當時為什麼這樣決定」。
而分歧本身就是最有價值的部分——它說明了哪些預測是錯的。
直接覆寫會把那段歷史一起刪掉。
系統長什麼樣,有別的東西負責:三份程式碼導讀、data/README.md、
以及程式碼本身。設計文件不需要再當一次現況文件,它會輸給程式碼,
而且是悄悄地輸——沒有任何機制會告訴你文件過期了。
所以做法改成:原文一字不動,在每個分歧處底下加一段引言,標明已被取代(日期) 並寫出理由。沒標的部分,就是至今仍然成立的部分。
文件開頭加一段讀法說明,因為一份半對半錯的文件,比全錯的更危險——
讀的人不知道該相信哪一半。
比對完之後,我發現它們不是同一種錯。
出題從「生成」變成「挑」。 設計寫的是模型產出 {title, description, tests}。
實作是從題庫挑 {chosen_id, reason}。理由是 Day 27 那條傳染鏈:
錯的測資會讓評測跟著錯,再經由摘要傳染給導師,學生最後對著一份正確的程式碼
被要求找出不存在的錯。
全部通過時不換題。 設計的圖是 grade → set_problem(跳過導師)。
實作是 grade → solved → tutor。一換題,那份「你過了」的判定就會被set_problem_node 清掉,學生什麼都沒看到;而且通過之後導師仍要開口說一句肯定。
錯誤觀念清單整批換掉。 從函式練習題的分類(dict-access、missing-return、recursion-base-case)換成 CPE 式的(io-eof、io-parse、output-format)。
這三處的共同點:當初的判斷在當初的前提下是對的,是前提變了。
設計文件有一節叫「沙箱的預留介面」,寫著未來的簽名會是:
make_grade_node(client) # 第 2 期:LLM 推測
make_grade_node(runner) # 未來:真實執行
實際上是 make_grade_node(client, runner=sandbox.run_all)——兩個都要。
方向猜對了(沙箱會換掉判定),但猜錯了一件事:以為模型會被淘汰,
實際上是換工作。執行器給事實,模型把失敗解讀成一句人話。
同一節下面那句「沙箱替換的只有『過/沒過』這個事實判斷,不是整個 agent」
其實已經講對了,只是上面的程式碼片段沒跟上。同一節裡的兩句話互相矛盾,
而寫的時候完全沒感覺。
TutorState 也類似:欄位名對,但少了四個,grade 的形狀從 3 個欄位長成 9 個。
這一種最值得記。設計文件的「已知限制」寫著:
評測是 AI 推測而非實際執行 ⇒ 會漏掉執行時才出現的錯誤,也可能誤判。
這條解決了。但解決它的東西帶來一條更嚴重的:
sandbox是執行器,不是安全沙箱 ⇒ 擋得住無窮迴圈與崩潰,
擋不住記憶體耗盡、網路、讀取其他檔案、fork 炸彈。
學生在自己電腦上跑自己寫的程式沒有新增風險;部署到雲端就是遠端程式碼執行。
在補上真正的隔離之前,公開部署必須設 runner=None。
第二條:「左欄文案不能聲稱執行了測試」——反過來了。
現在真的執行了,硬說成「推測」一樣是謊,而且會讓學生不敢相信真的結果。
現行規則是措辭跟著 source 走。
第三條是全新的:題庫會用完。 出題改成從題庫挑之後,題目做完就沒得換了。
解法是加題,不是改程式。
一條限制消失,換來兩條新的、一條反轉。「限制清單變短」從來不是進度的指標。
比對時還發現文件的兩張表停在開工那天。
黑板多了一個 collection:students/{學號}/solved/{題號}。
值得記的是它和 misconceptions 刻意不合併——clear_misconceptions
只清弱點統計,不動 solved:
做過哪些題是事實紀錄,弱點統計是推論。推論可以重來,事實不行。
檔案表少了四個模組,而它們有一個共同的出生理由:misconceptions.py、problems.py、sandbox.py——
每一個都是因為有多個地方必須講同一套規則才獨立出來的。
詞彙表要給導師、題庫、挑題三方共用;比對規則要同時給評測用、又要講給模型聽。
規則散在兩個地方時,不會報錯,只會安靜地不一致。
至於「只有 app.py 依賴 streamlit」這條約束,三期走完沒有破例。
第一天定下的約束,到最後仍然成立的沒有幾條,這是其中一條。
程式碼沒有動,只有文件。三期之後最大的一塊未完成工作已經寫在限制清單最上面:
真正的沙箱隔離。